/*
	purpose	:	to condense the d_voters array to only include the edge pixels that were
				used to generate a vote for a centroid that made it through the merging
				process. the centroids of concern are the ones included in d_finalCentroids.
				in the end, d_finalVoters will contain these edge pixels

	params	: 	d_finalVoters 	-- 	holds all the edge pixels that only voted for a centroid
									from mergeAccum()

				centroidVote	--	specifies which centroid the edge pixel voted for, this
									is an index into the d_finalCentroids array

	returns	: nothing
*/

__device__ void mergeVotes(point_list* d_finalVoters, int centroidVote, int* d_nearest_voters, int vote_length)
{
	int total_pixels = d_finalCentroids[centroidVote].votes * DATA_POINTS;
	int j = 0, end = DATA_POINTS * EVIDENCE_TRSH, temp;
	bool found = false;

	// gets the starting position in which to start placing votes,
	// as atomicAdd returns the old value of voteLocation. The range
	// starting at j will define the range in which the current thread
	// can place the votes for its' edge pixels
	j = atomicAdd(&voteLocation, total_pixels);

	//printf("index: %d, vote_length: %d, total_pixels: %d, j: %d\n", centroidVote, vote_length, total_pixels, j);

	for(int i = 0; i < vote_length; i++)
	{
		temp = d_nearest_voters[i];

		for(int k = 0; k < DATA_POINTS * EVIDENCE_TRSH; k++)
		{
			if(d_voters[k].centroidIndex == temp)
			{	
				d_finalVoters[j].point 			    = d_voters[k].point;
				d_finalVoters[j].centroid 			= d_voters[k].centroid;
				d_finalVoters[j++].centroidIndex 	= centroidVote;
				
/*				d_finalVoters[j].point 				= d_voters[temp * DATA_POINTS + 1].point;
				d_finalVoters[j].centroid 			= d_voters[temp * DATA_POINTS + 1].centroid;
				d_finalVoters[j++].centroidIndex 	= centroidVote;
				
				d_finalVoters[j].point 				= d_voters[temp * DATA_POINTS + 2].point;
				d_finalVoters[j].centroid 			= d_voters[temp * DATA_POINTS + 2].centroid;
				d_finalVoters[j++].centroidIndex 	= centroidVote;
				
				d_finalVoters[j].point 				= d_voters[temp * DATA_POINTS + 3].point;
				d_finalVoters[j].centroid 			= d_voters[temp * DATA_POINTS + 3].centroid;
				d_finalVoters[j++].centroidIndex 	= centroidVote;
*/
			//	found = true;
			}

		}

	//	found = false;
	}
}

/*
	params:	d_finalCentroids	--	holds the list of centroids after the merged centroid
									has been determined

			d_finalVoters		--	holds all the edge pixels that only voted for a centroid
									after mergeAccum()

	returns: nothing
*/

__global__ void mergeAccum(centroid_list* d_nearest, centroid_list* d_finCentroids, point_list* d_finalVoters)
{
	int 	index = blockIdx.x * blockDim.x + threadIdx.x,
			j = 0, 
			loc = 0,
			highestVoteCount = 0;
	double 	distance = 0.0;
	int 	d_nearest_voters[MAX_CENTROIDS] = {-1};
	int		totalVotes = d_possCentroids[index].votes;
	d_finalCentroids = d_finCentroids;

	int centroidLocation = 0;

//	if(d_possCentroids[index].votes > 1)
//		printf("thread id:  %d, vote count: %d\n", index, d_possCentroids[index].votes);

	//printf("index %d ... x: %f, y: %f, z: %f, ..... votes: %d\n", index, d_possCentroids[index].meanCentroid.x, d_possCentroids[index].meanCentroid.y, d_possCentroids[index].meanCentroid.z, d_possCentroids[index].votes);

	//only continue if vote count for potential centroid is above the min threshold
	if(d_possCentroids[index].votes >= CENTROID_TRSH_LOW)
	{
	//	printf("index %d ..... made it inside merging loop\n\n", index);
		//printf("mergeAccum() called .....\n");
		for(int i = 0; i < EVIDENCE_TRSH && totalVotes < CENTROID_TRSH_HIGH; i++)
		{
			if(i != index)// && d_possCentroids[i].votes >= CENTROID_TRSH_LOW)
			{
				distance = calcDistance(d_possCentroids[index].centroid, d_possCentroids[i].centroid);

				// if the centroid is within 5 of the currently examined centroid, add it to the list
				// of nearby centroids, and increment j (the position in nearest[])
				if(5.0 >= distance && i != index) //&& d_possCentroids[index].votes > d_possCentroids[i].votes)
				{
					d_nearest[index*MAX_CENTROIDS + j].centroid.x 		= d_possCentroids[i].centroid.x;
					d_nearest[index*MAX_CENTROIDS + j].centroid.y 		= d_possCentroids[i].centroid.y;
					d_nearest[index*MAX_CENTROIDS + j].centroid.z 		= d_possCentroids[i].centroid.z;

					d_nearest[index*MAX_CENTROIDS + j].meanCentroid.x 	= d_possCentroids[i].meanCentroid.x;
					d_nearest[index*MAX_CENTROIDS + j].meanCentroid.y 	= d_possCentroids[i].meanCentroid.y;
					d_nearest[index*MAX_CENTROIDS + j].meanCentroid.z 	= d_possCentroids[i].meanCentroid.z;
					d_nearest[index*MAX_CENTROIDS + j].votes 			= d_possCentroids[i].votes;

					totalVotes += d_possCentroids[i].votes;

					//keeps track of which indices are to be merged 
					//so later on the voters can be reassigned to the correct
					//centroid in the d_finalVoters array
					d_nearest_voters[j] = i;
				
					j++;
				}
			}
		}


		// insert the values of the centroid of the current thread to the end
		// of nearest[]
		d_nearest[index*MAX_CENTROIDS + j].centroid.x 		= d_possCentroids[index].centroid.x;
		d_nearest[index*MAX_CENTROIDS + j].centroid.y 		= d_possCentroids[index].centroid.y;
		d_nearest[index*MAX_CENTROIDS + j].centroid.z 		= d_possCentroids[index].centroid.z;

		d_nearest[index*MAX_CENTROIDS + j].meanCentroid.x 	= d_possCentroids[index].meanCentroid.x;
		d_nearest[index*MAX_CENTROIDS + j].meanCentroid.y 	= d_possCentroids[index].meanCentroid.y;
		d_nearest[index*MAX_CENTROIDS + j].meanCentroid.z 	= d_possCentroids[index].meanCentroid.z;
		d_nearest[index*MAX_CENTROIDS + j].votes 			= d_possCentroids[index].votes;

		d_nearest_voters[j] = index;
		j++;


//	for(int k = 0; k < j; k++)
//		printf("index: %d\n", d_nearest_voters[k]);

		// set highest vote count equal to the last element, which is the current
		// centroid being evaluated
		highestVoteCount = index*MAX_CENTROIDS;
		
		//printf("thread: %d ..... highest vote count before ..... %d\n\n", index, highestVoteCount);
	
		// find highest vote count position
		for(int k = 0; k < j; k++)
		{
			if(d_nearest[highestVoteCount].votes < d_nearest[index*MAX_CENTROIDS + k].votes)
				highestVoteCount = index*MAX_CENTROIDS + k;
		}
		
		
		//printf("thread: %d ...... highest vote count after: %d\n\n", index, highestVoteCount);
		
	
		//for(int i = 0; i < j; i++)
		//	printf("index: %d, value: %d\n", i, d_nearest_voters[i]);
		
		// merge if the highest vote count belongs to the centroid currently
		// being examined
		if(highestVoteCount == index*MAX_CENTROIDS + (j - 1))
		{
			// merge all votes and average centroids, except for the last element,
			// which is the current centroid being evaluated
			
			for(int k = 0; k < j-1; k++)
			{
				//if(d_possCentroids[index].votes < CENTROID_TRSH_HIGH)
				{
					d_possCentroids[index].votes += d_nearest[index*MAX_CENTROIDS + k].votes;
					d_possCentroids[index].meanCentroid.x = (d_possCentroids[index].meanCentroid.x + d_nearest[index*MAX_CENTROIDS + k].centroid.x) / 2.0;
					d_possCentroids[index].meanCentroid.y = (d_possCentroids[index].meanCentroid.y + d_nearest[index*MAX_CENTROIDS + k].centroid.y) / 2.0;
					d_possCentroids[index].meanCentroid.z = (d_possCentroids[index].meanCentroid.z + d_nearest[index*MAX_CENTROIDS + k].centroid.z) / 2.0;
				}
			}
			
			
			loc = atomicAdd(&d_centroidLocation, 1);

//			d_centroidLocation = centroidLocation;
			
//		printf("location: %d\n", centroidLocation);
			
			d_finalCentroids[loc].votes = d_possCentroids[index].votes;
			d_finalCentroids[loc].meanCentroid.x = d_possCentroids[index].meanCentroid.x;
			d_finalCentroids[loc].meanCentroid.y = d_possCentroids[index].meanCentroid.y;
			d_finalCentroids[loc].meanCentroid.z = d_possCentroids[index].meanCentroid.z;

//			printf("votes:  %d, x: %.8f, y:%.8f, z:%.8f\n", d_finalCentroids[loc].votes, d_finalCentroids[loc].meanCentroid.x,  d_finalCentroids[loc].meanCentroid.y,  d_finalCentroids[loc].meanCentroid.z);

			mergeVotes(d_finalVoters, loc, d_nearest_voters, j);
			
		}
	
	}
}


